package org.coco24.pmstudio

import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Screen
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.InputListener
import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable
import com.badlogic.gdx.utils.*
import com.badlogic.gdx.utils.Array
import com.kotcrab.vis.ui.widget.file.FileChooser
import com.kotcrab.vis.ui.widget.file.FileChooserListener
import com.kotcrab.vis.ui.widget.file.FileTypeFilter
import com.badlogic.gdx.utils.viewport.ScreenViewport
import com.kotcrab.vis.ui.VisUI
import com.kotcrab.vis.ui.util.adapter.AbstractListAdapter
import com.kotcrab.vis.ui.widget.*
import com.kotcrab.vis.ui.widget.tabbedpane.Tab
import com.kotcrab.vis.ui.widget.tabbedpane.TabbedPane
import com.kotcrab.vis.ui.widget.tabbedpane.TabbedPaneListener
import org.coco24.pmstudio.GdxGame.R
import org.coco24.pmstudio.`interface`.SimpleWindowCallback
import org.coco24.pmstudio.data.Project
import org.coco24.pmstudio.pack.BodyColor
import org.coco24.pmstudio.pack.Pokemon
import org.coco24.pmstudio.pack.PokemonForm
import org.coco24.pmstudio.pack.PokemonStats
import org.coco24.pmstudio.util.LeftTableBuilder
import org.coco24.pmstudio.util.UIUtils.dot2Float
import org.coco24.pmstudio.util.UIUtils.setOnChangeForeach
import org.coco24.pmstudio.util.UIUtils.setOnChanged
import org.coco24.pmstudio.util.UIUtils.updateList
import org.coco24.pmstudio.widget.EvolutionEntry
import org.coco24.pmstudio.widget.NamedOperationBar
import org.coco24.pmstudio.widget.OperationBar
import org.coco24.pmstudio.window.NewWindow
import java.io.ObjectOutputStream
import java.lang.IllegalStateException
import java.math.BigDecimal

class MainScreen: Screen
{
    val stage = Stage(ScreenViewport())
    val abilities = addNullValue(loadStringJson("data/abilities.json", "name"))
    val types = addNullValue(loadStringJson("data/types.json", "name"))
    val egggroups = addNullValue(loadStringJson("data/egggroups.json", "name"))
    val colors = addNullValue(loadStringJson("data/colors.json", "name"))
    val evolutions = loadEvolution()
    var currentPokemon: Pokemon? = null
    var currentForm: String? = null
    lateinit var updateRunnable: Runnable
    lateinit var pageChangedRunnable: Runnable
    lateinit var saveCurrentRunnable: Runnable
    lateinit var updateProjectRunnable: Runnable
    lateinit var updateCurrentPokemon: Runnable
    lateinit var updateFormTabRunnable: Runnable
    lateinit var changeFormRunnable: Runnable
    lateinit var project: Project
    var currentProjectFileHandler: FileHandle? = null
    override fun show()
    {
        //data initialization
        newProject()
        initUi()
        updateRunnable.run()
    }

    private fun newProject()
    {
        project = Project()
        currentProjectFileHandler = null
    }

    fun initUi()
    {
        stage.clear()
        val tableBackground = (VisUI.getSkin().get(VisImageButton.VisImageButtonStyle::class.java).up as NinePatchDrawable).tint(if (R.PPI < 128) Color.WHITE else Color.LIGHT_GRAY)
        val rootTable = Table()
        run {
            //Menu Bar
            val menuBar = MenuBar()
            run {
                val fileMenu = Menu("文件")
                run {
                    val newFileItem = MenuItem("新建工程")
                    run {
                        setOnChanged(newFileItem, Runnable {
                            newProject()
                            updateProjectRunnable.run()
                        })
                    }
                    val openFileItem = MenuItem("打开...")
                    run {
                        setOnChanged(openFileItem, Runnable {
                            requestFile("打开工程", "pdp", "Pokemon Database Project", object : FileChooserListener
                            {
                                override fun canceled()
                                {}

                                override fun selected(hbfiles: Array<FileHandle>?)
                                {
                                    if(hbfiles == null) throw IllegalStateException()
                                    project = Project.from(hbfiles[0])
                                    updateProjectRunnable.run()
                                    currentProjectFileHandler = hbfiles[0]
                                }

                            })
                        })
                    }
                    val saveFileItem = MenuItem("保存...")
                    run {
                        setOnChanged(saveFileItem, Runnable {
                            saveCurrentRunnable.run()
                            if(currentProjectFileHandler == null)
                            putFile("保存工程", "pdp", "Pokemon Database Project", object : FileChooserListener
                            {
                                override fun canceled()
                                {}

                                override fun selected(hbfiles: Array<FileHandle>?)
                                {
                                    if(hbfiles == null) throw IllegalStateException("未选中任何文件！")
                                    project.to(hbfiles[0])
                                    currentProjectFileHandler = hbfiles[0]
                                }

                            })
                            else
                                project.to(currentProjectFileHandler)
                        })
                    }
                    val saveAsItem = MenuItem("另存为...")
                    run {
                        setOnChanged(saveAsItem, Runnable {
                            saveCurrentRunnable.run()
                            putFile("工程另存为", "pdp", "Pokemon Database Project", object : FileChooserListener
                            {
                                override fun canceled()
                                {}

                                override fun selected(hbfiles: Array<FileHandle>?)
                                {
                                    if(hbfiles == null) throw IllegalStateException("未选中任何文件！")
                                    project.to(hbfiles[0])
                                    currentProjectFileHandler = hbfiles[0]
                                }

                            })
                        })
                    }
                    val importItem = MenuItem("导入")
                    run {
                        val importSubMenu = PopupMenu()
                        val importJsonFileMenu = MenuItem("导入Json文件...")
                        setOnChanged(importJsonFileMenu, Runnable {
                            putFile("导入Json文件", "json", "Json文件", object : FileChooserListener
                            {
                                override fun canceled()
                                {}

                                override fun selected(files: Array<FileHandle>?)
                                {}

                            })
                        })

                        importSubMenu.addItem(importJsonFileMenu)
                        importItem.subMenu = importSubMenu
                    }
                    val exportItem = MenuItem("导出")
                    run {
                        val exportSubMenu = PopupMenu()
                        val exportJsonFileMenu = MenuItem("导出Json文件...")
                        setOnChanged(exportJsonFileMenu, Runnable {
                            putFile("导出Json文件", "json", "Json文件", object : FileChooserListener
                            {
                                override fun canceled()
                                {}

                                override fun selected(files: Array<FileHandle>?)
                                {}

                            })
                        })


                        exportSubMenu.addItem(exportJsonFileMenu)
                        exportItem.subMenu = exportSubMenu
                    }
                    val exitItem = MenuItem("退出")
                    setOnChanged(exitItem, Runnable {Gdx.app.exit()})
                    fileMenu.addItem(newFileItem)
                    fileMenu.addItem(openFileItem)
                    fileMenu.addItem(saveFileItem)
                    fileMenu.addItem(saveAsItem)
                    fileMenu.addItem(importItem)
                    fileMenu.addItem(exportItem)
                    fileMenu.addSeparator()
                    fileMenu.addItem(exitItem)
//                    fileMenu.add(importSubMenu)
                }
                menuBar.addMenu(fileMenu)
            }
            //SubMenu
            val subMenuBar = Table()
            //SplitPane
            val mainTable = VisTable()
            run {
                val pokemonListTable = VisTable()
//                run {

                    val pmListAdapter = PokemonListAdapter(project.pokemons)

                    pmListAdapter.selectionMode = AbstractListAdapter.SelectionMode.SINGLE
                    val pmList = ListView<Pokemon>(pmListAdapter)
                    pmList.setItemClickListener {
                        saveCurrentRunnable.run()
                        updateCurrentPokemon.run()
                        pageChangedRunnable.run()
                    }

                    pmList.updatePolicy = ListView.UpdatePolicy.IMMEDIATELY

                    pmList.rebuildView()
                    pokemonListTable.add(pmList.mainTable).grow().row()

                    val operationBar = NamedOperationBar(pmListAdapter, object : NamedOperationBar.NamedOperationBarCallback<Pokemon> {
                        override fun getName(item: Pokemon): String = item.name.internal

                        override fun delete()
                        {
                            updateCurrentPokemon.run()
                            pageChangedRunnable.run()
                        }

                        override fun add(name: String): Pokemon
                        {
                            val pokemon = Pokemon(0)
                            pokemon.name.internal = name
                            project.pokemons.add(pokemon)
                            saveCurrentRunnable.run()
                            currentPokemon = pokemon
                            pageChangedRunnable.run()
                            return pokemon
                        }

                        override fun rename(item: Pokemon, name: String)
                        {
                            item.name.internal = name
                        }

                    })
                    operationBar.background = tableBackground
                pokemonListTable.add(operationBar).growX().bottom().expandY()
                pokemonListTable.pack()
                val pokemonInfoTable = VisTable()
                run {
                    val formTable = VisTable()
                    val formTabbedPane = TabbedPane()
                    formTabbedPane.addListener(object : TabbedPaneListener
                    {
                        override fun switchedTab(tab: Tab?)
                        {
                        }

                        override fun removedTab(tab: Tab?)
                        {
                            currentPokemon?.forms?.remove(tab?.tabTitle?:throw IllegalStateException("Tab名为空！"))
                            Gdx.app.postRunnable {
                                updateFormTabRunnable.run()
                                updateRunnable.run()
                            }

                        }

                        override fun removedAllTabs()
                        {
                        }

                    })
                    formTable.background = tableBackground
                    formTable.add(formTabbedPane.table).growX().left()
                    val addFormButton = VisTextButton("+")
                    setOnChanged(addFormButton, Runnable {
                        NewWindow("新建形态", object : SimpleWindowCallback {
                            override fun onCanceled()
                            {

                            }

                            override fun onConfirmed(obj: Any)
                            {
                                if(obj !is String)
                                    return
                                if(currentPokemon?.forms?.containsKey(obj)?:true)
                                    return
                                currentPokemon?.forms?.put(obj, PokemonForm())
                                saveCurrentRunnable.run()
                                updateFormTabRunnable.run()
                                updateRunnable.run()
//                                formTabbedPane.switchTab(formTabbedPane.tabs.size - 1)
//                                changeFormRunnable.run()
                            }

                        }).show(stage)
                    })
                    formTable.add(addFormButton).right()
                    formTable.pack()

                    val tabbedPane = TabbedPane()
                    tabbedPane.add(TextTab("基本信息"))
                    tabbedPane.add(TextTab("进化"))
                    tabbedPane.add(TextTab("技能表"))
                    tabbedPane.add(TextTab("其他"))
                    tabbedPane.switchTab(0)


                    val pokemonInfoBuilder = LeftTableBuilder()

                        val cnNameField = StringField("中文名称：")
                        val enNameField = StringField("英文名称：")
                        pokemonInfoBuilder.append(cnNameField, enNameField).row()

                    val cnFormNameField = StringField("形态中文名称：")
                    val enFormNameField = StringField("形态英文名称：")
                    pokemonInfoBuilder.append(cnFormNameField, enFormNameField).row()

                        val internationalNumberField = NumberField("全国编号：")
                        val siteNumber = NumberField("地区编号：")
                        pokemonInfoBuilder.append(internationalNumberField, siteNumber).row()

                        val typesNames = Array<String>()
                        types.forEach {
                            typesNames.add(it.second)
                        }

                        val type1SelectedBox = StringSelectBox("属性1：", typesNames)
                        val type2SelectedBox = StringSelectBox("属性2：", typesNames)
                        pokemonInfoBuilder.append(type1SelectedBox, type2SelectedBox).row()
                        val colorsNames = Array<String>()
                        colors.forEach {
                            colorsNames.add(it.second)
                        }
                        val colorSelectBox = StringSelectBox("颜色：", colorsNames)
                        pokemonInfoBuilder.append(colorSelectBox).row()

                        pokemonInfoBuilder.append(VisLabel("种族值：")).row()
                        val speciesStatsTable =  StatsTable()
                        pokemonInfoBuilder.append(speciesStatsTable).row()

                        pokemonInfoBuilder.append(VisLabel("基础点数：")).row()
                        val baseStatsTable = StatsTable()
                        pokemonInfoBuilder.append(baseStatsTable).row()

                        val heightField = StringField("身高：")
                        val weightField = StringField("体重：")
                        pokemonInfoBuilder.append(heightField, weightField).row()

                        val hatchField = NumberField("孵化周期：")
                        val catchRateField = NumberField("捕捉率：")
                        pokemonInfoBuilder.append(hatchField, catchRateField).row()

                        val eggGroupsNames = Array<String>()
                        egggroups.forEach {
                            eggGroupsNames.add(it.second)
                        }

                        val eggGroup1SelectBox = StringSelectBox("蛋组1：", eggGroupsNames)

                        val eggGroup2SelectBox = StringSelectBox("蛋组2：", eggGroupsNames)
                        pokemonInfoBuilder.append(eggGroup1SelectBox, eggGroup2SelectBox).row()

                        val abilitiesNames = Array<String>()
                        abilities.forEach {
                            abilitiesNames.add(it.second)
                        }
                        val ability1SelectBox = StringSelectBox("第一特性：", abilitiesNames)

                        val ability2SelectBox = StringSelectBox("第二特性：", abilitiesNames)
                        pokemonInfoBuilder.append(ability1SelectBox, ability2SelectBox).row()

                    val pokemonInfoContainer = pokemonInfoBuilder.build()

                    val evolutionContainer = VisTable()

                        val evolutionsTable = VisTable()
                        val addButton = VisTextButton("        +        ")
                        setOnChanged(addButton, Runnable {
                            val entry = EvolutionEntry(evolutions)
                            evolutionsTable.add(entry).left()
                            val deleteButton = VisTextButton("X")
                            evolutionsTable.add(deleteButton).row()
                            setOnChanged(deleteButton, Runnable {//TODO: 全刷新
                                evolutionsTable.removeActor(entry)
                                evolutionsTable.removeActor(deleteButton)
                            })
                        })
                        evolutionsTable.pack()
                        evolutionContainer.add(evolutionsTable).top().row()
                        evolutionContainer.add(addButton).expand().top()



                    val pokemonInfoPane = VisScrollPane(pokemonInfoContainer)
//                    pokemonInfoPane.listeners.removeIndex(1)
                    pokemonInfoPane.addListener(object : InputListener()
                    {
                        override fun scrolled(event: InputEvent?, x: Float, y: Float, amount: Int): Boolean
                        {
                                if (pokemonInfoPane.isScrollY) pokemonInfoPane.scrollY += 80 * amount
                                else if (pokemonInfoPane.isScrollX) //
                                    pokemonInfoPane.scrollX += 80 * amount
                            return true
                        }
                    })
                    pokemonInfoPane.setFadeScrollBars(false)
                    pokemonInfoPane.setFlickScroll(false)
                    pokemonInfoTable.add(formTable).fillX().row()
                    pokemonInfoTable.add(tabbedPane.table).fillX().row()
                    pokemonInfoTable.add(pokemonInfoPane).grow().row()
                    pokemonInfoTable.pack()

                    val tabs = Array.with(pokemonInfoContainer, evolutionContainer)
                    tabbedPane.addListener(object : TabbedPaneListener
                    {
                        override fun removedTab(tab: Tab?)
                        {

                        }

                        override fun removedAllTabs()
                        {
                        }

                        override fun switchedTab(tab: Tab?)
                        {

                        }
                    })
                    updateCurrentPokemon = Runnable {
                            currentPokemon = if(pmListAdapter.selection.size > 0 && !project.pokemons.isEmpty()) pmListAdapter.selection[0] else null
                            currentForm = if(currentPokemon?.forms?.containsKey("NORMAL")?:false) "NORMAL" else null
                    }
                    saveCurrentRunnable = Runnable {
                        val currentPokemon = currentPokemon
                        val currentForm = currentForm
                        if(currentPokemon == null || currentForm == null) return@Runnable

                        currentPokemon.name.locale["zh_CN"] = cnNameField.textField.text
                        currentPokemon.name.locale["en"] = enNameField.textField.text
                        currentPokemon.forms[currentForm]?.name?.locale?.put("zh_CN", cnFormNameField.textField.text)
                        currentPokemon.forms[currentForm]?.name?.locale?.put("en", enFormNameField.textField.text)

                        currentPokemon.forms[currentForm]?.type1 = types[type1SelectedBox.getSelectedIndex()].first
                        currentPokemon.forms[currentForm]?.type2 = types[type2SelectedBox.getSelectedIndex()].first
                        currentPokemon.number.internal = internationalNumberField.getNumber()
                        currentPokemon.color = colors[colorSelectBox.getSelectedIndex()].first
                        baseStatsTable.toStats(currentPokemon.baseStats)
                        speciesStatsTable.toStats(currentPokemon.forms[currentForm]?.speciesStats?:throw IllegalStateException())
                        currentPokemon.hatchTerm = hatchField.getNumber()
                        currentPokemon.eggGroup1 = egggroups[eggGroup1SelectBox.getSelectedIndex()].first
                        currentPokemon.eggGroup2 = egggroups[eggGroup2SelectBox.getSelectedIndex()].first
                        currentPokemon.forms[currentForm]?.ability1 = abilities[ability1SelectBox.getSelectedIndex()].first
                        currentPokemon.forms[currentForm]?.ability2 = abilities[ability2SelectBox.getSelectedIndex()].first
                        currentPokemon.catchRate = catchRateField.getNumber()
                        currentPokemon.height = heightField.textField.text.toFloatOrNull()?:0f
                        currentPokemon.weight = weightField.textField.text.toFloatOrNull()?:0f
                    }
                    pageChangedRunnable = Runnable {
                        updateList(pmListAdapter)
                        updateFormTabRunnable.run()
                        updateRunnable.run()
                    }
                    updateRunnable = Runnable {
                        val currentPokemon = currentPokemon
                        val currentForm = currentForm
                        pokemonInfoTable.isVisible = currentPokemon != null
                        tabbedPane.table.isVisible = currentForm != null
                        pokemonInfoContainer.isVisible = currentForm != null
                        if(currentPokemon == null || currentForm == null) return@Runnable

                        cnNameField.textField.text = currentPokemon.name.locale["zh_CN"]?:"???"
                        enNameField.textField.text = currentPokemon.name.locale["en"]?:"???"
                        cnFormNameField.textField.text = currentPokemon.forms[currentForm]?.name?.locale?.get("zh_CN")?:"???"
                        enFormNameField.textField.text = currentPokemon.forms[currentForm]?.name?.locale?.get("en")?:"???"

                        type1SelectedBox.visSelectBox.selectedIndex = pairFirstIndexOf(types, currentPokemon.forms[currentForm]?.type1?:"NULL")
                        type2SelectedBox.visSelectBox.selectedIndex = pairFirstIndexOf(types, currentPokemon.forms[currentForm]?.type2?:"NULL")
                        internationalNumberField.textField.text = currentPokemon.number.internal.toString();
                        baseStatsTable.fromStats(currentPokemon.baseStats)
                        speciesStatsTable.fromStats(currentPokemon.forms[currentForm]?.speciesStats?:PokemonStats())
                        hatchField.textField.text = currentPokemon.hatchTerm.toString();
                        colorSelectBox.visSelectBox.selectedIndex = pairFirstIndexOf(colors, currentPokemon.color?:"NULL")
                        eggGroup1SelectBox.visSelectBox.selectedIndex = pairFirstIndexOf(egggroups, currentPokemon.eggGroup1)
                        eggGroup2SelectBox.visSelectBox.selectedIndex = pairFirstIndexOf(egggroups, currentPokemon.eggGroup2)
                        ability1SelectBox.visSelectBox.selectedIndex = pairFirstIndexOf(abilities, currentPokemon.forms[currentForm]?.ability1?:"NULL")
                        ability2SelectBox.visSelectBox.selectedIndex = pairFirstIndexOf(abilities, currentPokemon.forms[currentForm]?.ability2?:"NULL")

                        weightField.textField.text = dot2Float(currentPokemon.weight, 1)
                        heightField.textField.text = dot2Float(currentPokemon.height, 1)
                        catchRateField.textField.text = currentPokemon.catchRate.toString()
                    }
                    updateProjectRunnable = Runnable {
                        pmListAdapter.clear()
                        pmListAdapter.addAll(project.pokemons)
                        pmList.rebuildView()
                        pmListAdapter.selection.clear()
                        updateCurrentPokemon.run()
                        pageChangedRunnable.run()
                    }
                    changeFormRunnable = Runnable {
                        saveCurrentRunnable.run()
                        currentForm = formTabbedPane.activeTab.tabTitle
                        updateRunnable.run()
                    }
                    updateFormTabRunnable = Runnable {
                        formTabbedPane.removeAll()
                        currentPokemon?.forms?.toList()?.reversed()?.forEach { //keys?.toTypedArray()?.reversedArray()?.forEach{
                            formTabbedPane.add(PokemonFormTab(it.first))
                        }
                        setOnChangeForeach(formTabbedPane.table, Runnable {
                            changeFormRunnable.run()
                        })
                        if(formTabbedPane.tabs.size > 0)
                        {
                            formTabbedPane.switchTab(0)
                            currentForm = formTabbedPane.activeTab?.tabTitle
                        }
                        else currentForm = null
                    }

                }
                val splitPane = MultiSplitPane(false)
                splitPane.setWidgets(pokemonListTable, pokemonInfoTable)
                splitPane.setSplit(0, 0.2f)
                mainTable.add(splitPane).grow()
                mainTable.pack()
            }

            rootTable.setFillParent(true)
            rootTable.add(menuBar.table).fillX().top().row()
            rootTable.add(subMenuBar).fillX().top().row()
            rootTable.add(mainTable).grow().row()
            rootTable.pack()
            stage.addActor(rootTable)
            Gdx.input.inputProcessor = stage;
            (stage.viewport as ScreenViewport).unitsPerPixel = R.unitsPerPixel
            menuBar.table.background = tableBackground
            subMenuBar.background = tableBackground
        }
        pageChangedRunnable.run()

    }
    fun addNullValue(array: Array<Pair<String, String>>): Array<Pair<String, String>>
    {
        array.insert(0, Pair("NULL", "无"))
        return array
    }
    private fun loadStringJson(file: String, parameter: String): Array<Pair<String, String>>
    {
        val array = Array<Pair<String, String>>() //internal_name, showing_name
        val reader = JsonReader()
        val value = reader.parse(Gdx.files.internal(file).reader("UTF-8"))
        value.forEach {
            array.add(Pair(it.name(), it.get(parameter).getString("zh_CN")))
        }
        return array
    }
    fun loadEvolution(): Array<Triple<String, String, Any>>
    {
        fun recur(jsonObject: JsonValue, parent: Array<Triple<String, String, Any>>) //方式内部名称，方式名称，方式
        {
            jsonObject.forEach {
                if(it.isObject)
                {
                    val v = it.get("value")
                    if(v.isObject)
                    {
                        val a = Array<Triple<String, String, Any>>()
                        val t = Triple(it.name(), it.get("name").getString("zh_CN"), a)
                        recur(v, a)
                        parent.add(t)
                    }
                    else
                    {
                        val a = it.getString("value")
                        val t = Triple(it.name(), it.get("name").getString("zh_CN"), a)
                        parent.add(t)
                    }

                }
            }
        }

        val array = Array<Triple<String, String, Any>>() //internal_name, showing_name
        val reader = JsonReader()
        val value = reader.parse(Gdx.files.internal("data/evolutions.json").reader("UTF-8"))
        recur(value, array)
        return array
    }

    class StringSelectBox(name: String, array: Array<String>): VisTable()
    {
        val visSelectBox = VisSelectBox<String>()
        init
        {
            add(name)
            visSelectBox.items = array
            add(visSelectBox).pad(2f)
            pack()
        }
        fun getSelectedIndex(): Int
        {
            return visSelectBox.selectedIndex
        }
    }
    class NumberField(name: String, value: Int = 0): VisTable()
    {
        val textField = VisTextField(value.toString())
        val textFieldCell: Cell<VisTextField>
        init
        {
            textField.setTextFieldFilter(VisTextField.TextFieldFilter.DigitsOnlyFilter())
            add(name)
            textFieldCell = add(textField).pad(5f)
            pack()
        }
        fun getNumber(): Int
        {
            return textField.text.toIntOrNull()?:0
        }
    }
    class StringField(name: String, value: String = ""): VisTable()
    {
        val textField = VisTextField(value)
        val textFieldCell: Cell<VisTextField>
        init
        {
            add(name)
            textFieldCell = add(textField).pad(5f)
            pack()
        }
        fun getString(): String
        {
            return textField.text
        }
    }
    fun requestFile(title: String, extensions: String, extensionsComment: String, listener: FileChooserListener)
    {
        val fileChooser = FileChooser(title, FileChooser.Mode.OPEN)
        val fileFilter = FileTypeFilter(true)
        fileFilter.addRule("$extensionsComment (*.$extensions)", extensions)
        fileChooser.setFileTypeFilter(fileFilter)
        fileChooser.setListener(object : FileChooserListener
        {
            override fun selected(files: Array<FileHandle>?)
            {
                if(files == null) return
                if(files.size > 0)
                {
                    listener.selected(files)
                }
                fileChooser.fadeOut()
            }
            override fun canceled() = fileChooser.fadeOut()
        })
        fileChooser.width = 900f
        stage.addActor(fileChooser)

        fileChooser.fadeIn()
    }
    fun putFile(title: String, extensions: String, extensionsComment: String, listener: FileChooserListener)
    {
        val fileChooser = FileChooser(title, FileChooser.Mode.SAVE)
        val fileFilter = FileTypeFilter(true)
        fileFilter.addRule("$extensionsComment (*.$extensions)", extensions)
        fileChooser.setFileTypeFilter(fileFilter)
        fileChooser.setListener(object : FileChooserListener
        {
            override fun selected(files: Array<FileHandle>?)
            {
                if(files == null) return
                if(files.size > 0)
                {
                    listener.selected(files)
                }
                fileChooser.fadeOut()
            }
            override fun canceled() = fileChooser.fadeOut()
        })
        fileChooser.setDefaultFileName("未命名.$extensions")
        fileChooser.width = 900f
        stage.addActor(fileChooser)
        fileChooser.fadeIn()
    }
    override fun render(delta: Float)
    {
        stage.act();
        stage.draw();
    }
    override fun pause()
    {
    }
    override fun resume()
    {
    }
    override fun resize(width: Int, height: Int)
    {
        if (width == 0 && height == 0) return  //see https://github.com/libgdx/libgdx/issues/3673#issuecomment-177606278
        stage.viewport.update(width, height, true)
    }
    override fun dispose()
    {
    }
    override fun hide()
    {
    }
    class StatsTable(): VisTable()
    {
        val hpField = NumberField("体力：", 0)
        val atkField = NumberField("攻击：", 0)
        val defField = NumberField("防御：", 0)
        val spAtkField = NumberField("特攻：", 0)
        val spDefField = NumberField("特防：", 0)
        val speedField = NumberField("速度：", 0)
        init
        {
            add(hpField)
            add(atkField).row()
            add(defField)
            add(spAtkField).row()
            add(spDefField)
            add(speedField)
            pack()
        }
        fun toStats(pokemonStats: PokemonStats)
        {
            pokemonStats.attack = atkField.getNumber()
            pokemonStats.defense = defField.getNumber()
            pokemonStats.hp = hpField.getNumber()
            pokemonStats.specialAttack = spAtkField.getNumber()
            pokemonStats.specialDenfense = spDefField.getNumber()
            pokemonStats.speed = speedField.getNumber()
        }
        fun fromStats(pokemonStats: PokemonStats)
        {
            atkField.textField.text = pokemonStats.attack.toString();
            defField.textField.text = pokemonStats.defense.toString();
            hpField.textField.text = pokemonStats.hp.toString();
            spAtkField.textField.text = pokemonStats.specialAttack.toString();
            spDefField.textField.text = pokemonStats.specialDenfense.toString();
            speedField.textField.text = pokemonStats.speed.toString();

        }
    }
    fun pairFirstIndexOf(array: Array<Pair<String, String>>, first: Any): Int
    {
        var i = 0
        array.forEach {
            if(it.first == first)
                return i
            i++
        }
        return -1
    }

}